#ifndef MTL_THREAD_HPP
#define MTL_THREAD_HPP

extern "C" {
#include <unistd.h>
#include <pthread.h>
}
#include <ostream>
#include "exception.hpp"
#include "functional.hpp"
#include "memory.hpp"
#include "mutex.hpp"
#include "condition_variable.hpp"

namespace mtl {

  class func_adapter {
  public:
    virtual ~func_adapter() {}
    virtual void* operator()() = 0;
  };

  namespace {
    template<class R, class F> class func_adapter_impl : public func_adapter {
    public:
      func_adapter_impl(F& f) : m_f(f) {}
      void* operator()() {
        return reinterpret_cast<void*>(m_f());
      }
    private:
      F m_f;
    };

    template<class F> class func_adapter_impl<void, F> : public func_adapter {
    public:
      func_adapter_impl(F& f) : m_f(f) {}
      void* operator()() {
        m_f();
        return NULL;
      }
    private:
      F m_f;
    };

    // root of create_adapter
    template<class _Bind>
    func_adapter* _create_adapter(_Bind b) {
      return new func_adapter_impl<typename _Bind::result_type, _Bind>(b);
    }

    /* C++0x
    template<class _Func, class... _Args>
    func_adapter* create_adapter(_Func f, _Args... args) {
      return _create_adapter(mtl::bind(f, args...));
    }*/
    template<class F>
      func_adapter* create_adapter(F f) {
        return _create_adapter(mtl::bind(f));
      }
    template<class F, class Arg1>
      func_adapter* create_adapter(F f, Arg1 a1) {
        return _create_adapter(mtl::bind(f, a1));
      }
    template<class F, class Arg1, class Arg2>
      func_adapter* create_adapter(F f, Arg1 a1, Arg2 a2) {
        return _create_adapter(mtl::bind(f, a1, a2));
      }
    template<class F, class Arg1, class Arg2, class Arg3>
      func_adapter* create_adapter(F f, Arg1 a1, Arg2 a2, Arg3 a3) {
        return _create_adapter(mtl::bind(f, a1, a2, a3));
      }
    template<class F, class Arg1, class Arg2, class Arg3, class Arg4>
      func_adapter* create_adapter(F f, Arg1 a1, Arg2 a2, Arg3 a3, Arg4 a4) {
        return _create_adapter(mtl::bind(f, a1, a2, a3, a4));
      }

  }// 無名

  struct thread_context {
    typedef pthread_t native_handle_type;

    static const native_handle_type invalid_native_handle = -1;

    explicit thread_context(func_adapter* func)
      : m_func(func), m_handle(-1), m_joined(false), m_done(false)
    {}

    recursive_mutex           m_mtx;
    shared_ptr<func_adapter>  m_func;
    native_handle_type        m_handle;
    bool                      m_joined;
    condition_variable        m_done_cond;
    bool                      m_done;
  };

  typedef shared_ptr<thread_context> thread_context_ptr;

  class thread {
  public:
    typedef pthread_t native_handle_type;

    class id {
    public:
      id() : m_h(thread_context::invalid_native_handle) {} // 実行スレッドを表さない ID

      typedef thread::native_handle_type  native_handle_type;
      id(const native_handle_type& h) : m_h(h) {}
      native_handle_type native_handle() const { return m_h; }

      friend bool operator==(const thread::id&, const thread::id&);
      friend bool operator!=(const thread::id&, const thread::id&);
      friend bool operator< (const thread::id&, const thread::id&);
      friend bool operator<=(const thread::id&, const thread::id&);
      friend bool operator> (const thread::id&, const thread::id&);
      friend bool operator>=(const thread::id&, const thread::id&);
    private:
      native_handle_type  m_h;
    };

    thread() {} // 何に使うんだろうな？

    /* C++0x
    template<class F, class ...Args> explicit thread(F f, Args... args)
      : m_context_ptr(
          new thread_context(create_adapter(f, args...)))
    {
      m_context_ptr->m_handle = create(this);
    }*/

    template<class F>
      explicit thread(F f)
        : m_context_ptr(new thread_context(create_adapter(f)))
      { on_constructor(); }

    template<class F, class Arg1>
      explicit thread(F f, Arg1 a1)
        : m_context_ptr(new thread_context(create_adapter(f, a1)))
      { on_constructor(); }

    template<class F, class Arg1, class Arg2>
      explicit thread(F f, Arg1 a1, Arg2 a2)
        : m_context_ptr(new thread_context(create_adapter(f, a1, a2)))
      { on_constructor(); }

    template<class F, class Arg1, class Arg2, class Arg3>
      explicit thread(F f, Arg1 a1, Arg2 a2, Arg3 a3)
        : m_context_ptr(new thread_context(create_adapter(f, a1, a2, a3)))
      { on_constructor(); }

    template<class F, class Arg1, class Arg2, class Arg3, class Arg4>
      explicit thread(F f, Arg1 a1, Arg2 a2, Arg3 a3, Arg4 a4)
        : m_context_ptr(new thread_context(create_adapter(f, a1, a2, a3, a4)))
      { on_constructor(); }

    ~thread() {
      if (joinable()) {
        std::terminate(); // TODO いいのか？
      }
    }

    thread_context_ptr get_context_ptr() const {
      lock_guard<mutex> lock(m_context_mutex);
      return m_context_ptr;
    }

    void swap(thread& t) {
      m_context_ptr.swap(t.m_context_ptr);
    }

    bool joinable() const {
      return get_context_ptr();
    }

    void join();

    void detach() {
      thread_context_ptr lcontext;
      {
        lock_guard<mutex> lock(m_context_mutex);
        lcontext.swap(m_context_ptr);
      }
      if (!lcontext) {
        throw system_error(
            error_code(invalid_argument, system_category()),
            "not joinable.");
      }
      PTHREAD_VALIDATE(
        pthread_detach(lcontext->m_handle), "failed to detach");
    }

    id get_id() const {
      thread_context_ptr const c = get_context_ptr();
      if (c) {
        return id(c->m_handle);
      } else {
        return id();
      }
    }

    native_handle_type native_handle() {
      return get_id().native_handle();
    }

    static unsigned hardware_concurrency() {
#if defined(_SC_NPROCESSORS_ONLN)
      int const count = sysconf(_SC_NPROCESSORS_ONLN);
      return (count>0)?count:0;
#else
      return 0;
#endif
    }

  private:
    thread(const thread&);
    thread& operator=(const thread&);

  private:
    mutable mutex       m_context_mutex;
    thread_context_ptr  m_context_ptr;

    mutable recursive_mutex   m_mtx;
    shared_ptr<func_adapter>  m_func;
    id                        m_id;
    bool                      m_joined;
    condition_variable        m_done_cond;
    bool                      m_done;

  protected:
    void on_constructor() {
      m_context_ptr->m_handle = create(this);
    }

    static void* run(void* self) {
      void* ret = NULL;
      pthread_cleanup_push(thread::cleanup, self);
        thread* const th = reinterpret_cast<thread*>(self);
        ret = (*th->m_context_ptr->m_func)();
      pthread_cleanup_pop(1);
      return ret;
    }

    static void cleanup(void* self) {
      thread* const th = reinterpret_cast<thread*>(self);
      lock_guard<mutex> lock(th->m_context_ptr->m_mtx);
      th->m_context_ptr->m_func.reset();
      th->m_context_ptr->m_done = true;
      th->m_context_ptr->m_done_cond.notify_all();
    }

    static native_handle_type create(void* p) {
      pthread_t h;
      PTHREAD_VALIDATE(
        pthread_create(&h, NULL, thread::run, p),
          "failed to create thread");
      return h;
    }

  public:
    bool interrupt() {
      return interrupt(get_id());
    }

    static bool interrupt(const thread::id& id) {
      // 成功：true，失敗：false
      return pthread_cancel(id.native_handle()) == 0;
    }

    static bool equals(const native_handle_type& l, const native_handle_type& r) {
      return pthread_equal(l, r) != 0;
    }
  };

  template<class charT, class traits>
    std::basic_ostream<charT, traits>&
      operator<<(std::basic_ostream<charT, traits>& out, const thread::id& id) {
        return out << id.native_handle();
    }

  namespace this_thread {
    thread::id get_id();
    void yield();
    /*template<class Clock, class Duration>
      void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);
    template<class Rep, class Period>
      void sleep_for(const chrono::duration<Rep, Period>& rel_time);*/
    // not included in c++0x
    void check_cancel();
  }

}// end namespace mtl
#endif

